<?php

namespace core;

/**
 * Engine core
 *
 */
class Core extends \patterns\Singleton
{
	/************************************************************
	*   					Properties							*
	************************************************************/


	/**
	 * Array of service adapter objects
	 */
	private $services = array();

	/**
	 * Array of sytem class objects
	 */
	private $objects = array();



	/************************************************************
	*   						*structors						*
	************************************************************/


	/**
	 * This class should be singleton, so make the constructor protected
	 *
	 */
	protected function __construct()
	{

	}

	public function __destruct()
	{
		if (!defined("_FINISHED"))
		{
			die();//Emergency stop
		}

		service('debug')->GenericStats();
		service('debug')->ServicesStats();
		service('debug')->DbStats();
		service('debug')->ComponentsStats();

		$cont = ob_get_clean();

		$cont = core('Html')->Tidy($cont);

		print $cont;
	}




	/************************************************************
	*   						Magic							*
	************************************************************/




	/************************************************************
	*   					Public methods						*
	************************************************************/


	/**
	 * Main engine method. Setting up stuff.
	 *
	 */
	public function Start()
	{
		ob_start();

		session_start();

		core("Registry")->Set('startup', microtime(1), 'timer');

		header('Content-Type: text/html; charset=UTF-8');

		//Headers
		//header("expires: mon, 26 jul 1997 05:00:00 gmt"); //дата в прошлом

		/**
		 * TODO:
		 * load config 					V
		 * set up Navigation data		V
		 * set up system variables		?
		 * validate form if any
		 * start up page parser
		*/


		/*
		///Testing DB service adapter
		$db = core("db");

		$res = $db->
		Select("name", "mail")->
		From("_users")->
		Join("_users_types", "INNER")->
		On("_users_types.id", "=", "_users.type")->
		On("_users_types.id", "!=", 24, "AND")->
		Where("_users_types.id", ">", 1)->
		Random()->
		Run();

		print "<pre>";
		print_r($res);
		print "</pre>";

		die();
		*/

		//Generate page
		print core('Page')->Start();

		define("_FINISHED", true);
	}


	public function GetService($serviceName)
	{
		$services = $this->GetServices();

		if (in_array($serviceName, $services))
		{
			return $this->StartService($serviceName);
		}
		else
		{
			die("Service $serviceName not set");
		}
	}

	public function GetObject($className)
	{
		if (!isset($this->objects[$className]))
		{
			$this->objects[$className] = $this->NewObject("\core\\".$className);
		}

		return $this->objects[$className];
	}


	/**
	 * Checks against specific types and halts if something is wrong
	 *
	 * @param	string	$type
	 * @param	string	$var1, $var2, ...
	 * @return	boolean
 	 */
	public function CheckType($var, $types)
	{
		$vars = func_get_args();
		$var = array_shift($vars);
		$types = $vars;

		$actualType = gettype($var);

		if (!in_array($actualType, $types))
		{
			$last = array_pop($types);

			if (count($types))
			{
				$last = " or $last";
			}

			$types = implode(", ", $types)." $last";

			service('debug')->error("Incorrect data type($actualType). Expected $types");
		}

		return true;
	}

	/**
	 * Returns true if length of input string was within the borders indicated
	 *
	 * @param string $string
	 * @param integer $min
	 * @param integer $max
	 * @return boolean
	 */
	public function CheckLength($string, $min = 0, $max = null)
	{
		$length = strlen($string);

		if ($length >= (int)$min && ($length <= (int)$max || is_null($max)))
		{
			return true;
		}
		else
		{
			service('debug')->error("String '$string' shold be at least $length characters long.");
		}
	}

	/**
	 * Checks if array has certain values
	 *
	 * @param array $array
	 * @param string $key
	 * @return boolean
	 */
	public function CheckArrayValues($array, $key)
	{
		$vars = func_get_args();
		$array = array_shift($vars);
		$keys = $vars;

		$interesct = array_intersect($keys, $array);

		if (count($interesct) >= count($keys))
		{
			return true;
		}
		else
		{
			service('debug')->error("Array must contain these values: ".implode(", ", $keys));
		}
	}

	/**
	 * Checks if array has certain keys
	 *
	 * @param array $array
	 * @param string $key
	 * @return boolean
	 */
	public function CheckArrayKeys($array, $key)
	{
		$vars = func_get_args();
		$array = array_keys(array_shift($vars));
		$keys = $vars;

		$interesct = array_intersect_key($keys, $array);

		if (count($interesct) >= count($keys))
		{
			return true;
		}
		else
		{
			service('debug')->error("Array must contain these keys: ".implode(", ", $keys));
		}
	}

	/**
	 * Factory method. Creates new object deciding which class to use.
	 *
	 * @param string $class
	 */
	public function NewObject($class, $param = null)
	{
		$classArray = array_filter(explode("\\", $class));
		$class = implode("\\", $classArray);

		$path = _PROJECT_CLASSES_DIR.str_replace("\\", "/", $class).".class.php";

		return new $class($param);
	}




	/************************************************************
	*   					Private Methods						*
	************************************************************/


	/**
	 * Starts a service
	 *
	 * @param string $service
	 * @return boolean
	 */
	protected function StartService($service)
	{
		if ($this->IsServiceStarted($service))
		{
			return $this->services[$service];
		}

		$serviceConf = core('Config')->GetConfig($service);

		//No config?
		if (!$serviceConf || !isset($serviceConf['_provider']))
		{
			return new \patterns\Null();
		}

		$this->services[$service] = $this->NewObject("\adapters\\" . $serviceConf['_provider'], $serviceConf);

		$this->CheckType($this->services[$service], "object");

		return $this->services[$service];
	}




	/************************************************************
	*   					Private Methods						*
	************************************************************/


	/**
	 * Checks whether service has been already started
	 *
	 */
	private function IsServiceStarted($service)
	{
		if (in_array($service, $this->GetServices()))
		{
			if (isset($this->services[$service]))
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		else
		{
			return null;
		}
	}

	private function GetFuncParams($paramName, $methodParams)
	{
		$params = array();
		$params[$paramName] = array_shift($methodParams);
		return array_merge($params, $methodParams);
	}

	/**
	 * Returns array of available services types
	 *
	 * @return array
	 */
	private function GetServices()
	{
		static $services = array();

		if (empty($services))
		{
			$folders = scandir(_SYSTEM_DIR."/services/");

			foreach ($folders as &$item)
			{
				if ($item !== "." && $item !== "..")
				{
					$services[] = $item;
				}
			}
		}

		return $services;
	}

	private function ParseForVars()
	{
	}
}